#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>

// 引入嵌入的曲目数据
#include "merged_tracks.h"

// 曲目结构体
typedef struct {
    char *name;
    char *download_url;
} Track;

// 动态数组
typedef struct {
    Track *items;
    size_t count;
    size_t capacity;
} TrackList;

// -------------------------- 工具函数：字符串处理 --------------------------
void tracklist_init(TrackList *list) {
    list->items = NULL;
    list->count = 0;
    list->capacity = 0;
}

void tracklist_add(TrackList *list, const char *name, const char *download_url) {
    if (name == NULL || strlen(name) == 0) {
        fprintf(stderr, "警告：跳过无效曲目：名称为空（URL：%s）\n", download_url);
        return;
    }
    if (download_url == NULL || strlen(download_url) == 0) {
        fprintf(stderr, "警告：跳过无效曲目：URL为空（名称：%s）\n", name);
        return;
    }

    if (list->count >= list->capacity) {
        size_t new_cap = (list->capacity == 0) ? 16 : list->capacity * 2;
        Track *new_items = realloc(list->items, new_cap * sizeof(Track));
        if (!new_items) {
            perror("错误：内存分配失败");
            exit(EXIT_FAILURE);
        }
        list->items = new_items;
        list->capacity = new_cap;
    }

    list->items[list->count].name = strdup(name);
    list->items[list->count].download_url = strdup(download_url);
    list->count++;
}

void tracklist_free(TrackList *list) {
    for (size_t i = 0; i < list->count; i++) {
        free(list->items[i].name);
        free(list->items[i].download_url);
    }
    free(list->items);
    list->items = NULL;
    list->count = 0;
    list->capacity = 0;
}

void trim_string(char *str) {
    if (!str || *str == '\0') return;

    // 移除尾部空格、\t、\n、\r
    size_t len = strlen(str);
    while (len > 0) {
        char c = str[len - 1];
        if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
            len--;
        } else {
            break;
        }
    }
    str[len] = '\0';

    // 移除头部空格、\t
    size_t start = 0;
    while (str[start] == ' ' || str[start] == '\t') {
        start++;
    }

    if (start > 0) {
        memmove(str, str + start, len - start + 1);
    }
}

// -------------------------- 核心：清理残留 ffplay 进程（无 system） --------------------------
// 通过 /proc 目录查找并终止所有 ffplay 进程
void clean_ffplay_residues() {
    printf("检查并清理残留的 ffplay 进程...\n");
    DIR *proc_dir = opendir("/proc");
    if (!proc_dir) {
        perror("警告：无法打开 /proc 目录（跳过进程清理）");
        return;
    }

    struct dirent *entry;
    int killed_count = 0;

    // 遍历 /proc 下的进程目录（名称为数字的目录即进程ID）
    while ((entry = readdir(proc_dir)) != NULL) {
        if (!isdigit((unsigned char)entry->d_name[0])) {
            continue; // 跳过非数字目录（非进程）
        }

        // 拼接进程名路径：/proc/[pid]/comm（comm 文件存储进程名）
        char comm_path[PATH_MAX];
        snprintf(comm_path, sizeof(comm_path), "/proc/%s/comm", entry->d_name);

        // 读取进程名
        FILE *comm_file = fopen(comm_path, "r");
        if (!comm_file) {
            continue; // 进程可能已退出，忽略
        }

        char comm[256];
        if (fgets(comm, sizeof(comm), comm_file)) {
            trim_string(comm);
            // 匹配 ffplay 进程
            if (strcmp(comm, "ffplay") == 0) {
                pid_t pid = atoi(entry->d_name);
                if (pid <= 0) {
                    fclose(comm_file);
                    continue;
                }
                // 发送终止信号（SIGTERM）
                if (kill(pid, SIGTERM) == 0) {
                    killed_count++;
                    printf("已终止 ffplay 进程（PID：%d）\n", pid);
                } else if (errno != ESRCH) { // 忽略"进程不存在"错误（可能刚退出）
                    fprintf(stderr, "警告：无法终止进程 %d：%s\n", pid, strerror(errno));
                }
            }
        }
        fclose(comm_file);
    }

    closedir(proc_dir);
    if (killed_count == 0) {
        printf("无残留 ffplay 进程\n");
    } else {
        printf("共终止 %d 个 ffplay 残留进程\n", killed_count);
    }
}

// -------------------------- 工具函数：执行外部命令（fork + execvp，替代 system） --------------------------
// 执行外部命令，返回子进程退出状态（-1 表示执行失败）
int execute_command(char *const args[]) {
    pid_t pid = fork();
    if (pid == -1) {
        perror("错误：fork 失败");
        return -1;
    }

    if (pid == 0) { // 子进程：执行命令
        execvp(args[0], args);
        // execvp 失败才会执行到这里
        perror("错误：命令执行失败");
        exit(EXIT_FAILURE);
    } else { // 父进程：等待子进程结束
        int status;
        if (waitpid(pid, &status, 0) == -1) {
            perror("错误：等待子进程失败");
            return -1;
        }
        if (WIFEXITED(status)) {
            return WEXITSTATUS(status); // 返回命令退出码
        } else {
            fprintf(stderr, "警告：子进程异常终止\n");
            return -1;
        }
    }
}

// -------------------------- 核心：解析嵌入的曲目数据 --------------------------
void parse_embedded_tracks(TrackList *tracks) {
    char *tracks_buf = malloc(merged_tracks_txt_len + 1);
    if (!tracks_buf) {
        perror("错误：分配曲目缓冲区失败");
        exit(EXIT_FAILURE);
    }
    memcpy(tracks_buf, merged_tracks_txt, merged_tracks_txt_len);
    tracks_buf[merged_tracks_txt_len] = '\0';

    char line[2048];
    size_t line_num = 0;
    size_t valid_count = 0;
    size_t skip_count = 0;

    printf("开始解析嵌入的曲目数据...\n");

    char *token = strtok(tracks_buf, "\n\r");
    while (token != NULL) {
        line_num++;
        strncpy(line, token, sizeof(line) - 1);
        line[sizeof(line) - 1] = '\0';
        trim_string(line);

        if (strlen(line) == 0) {
            skip_count++;
            token = strtok(NULL, "\n\r");
            continue;
        }

        char *sep = strchr(line, '!');
        if (!sep) {
            fprintf(stderr, "警告：第%d行格式错误：无 '!' 分隔符（跳过）\n", (int)line_num);
            skip_count++;
            token = strtok(NULL, "\n\r");
            continue;
        }

        *sep = '\0';
        char *name_part = line;
        char *url_part = sep + 1;
        trim_string(name_part);
        trim_string(url_part);

        if (strlen(name_part) == 0 || strlen(url_part) == 0) {
            fprintf(stderr, "警告：第%d行无效：名称或URL为空（跳过）\n", (int)line_num);
            skip_count++;
            token = strtok(NULL, "\n\r");
            continue;
        }

        tracklist_add(tracks, name_part, url_part);
        valid_count++;
        if (valid_count % 100 == 0) {
            printf("已解析 %d 首曲目...\n", (int)valid_count);
        }

        token = strtok(NULL, "\n\r");
    }

    free(tracks_buf);

    printf("\n嵌入曲目数据解析完成\n");
    printf("总行数：%d | 有效曲目：%d | 跳过无效行：%d\n", 
           (int)(valid_count + skip_count), (int)valid_count, (int)skip_count);

    if (valid_count == 0) {
        fprintf(stderr, "错误：嵌入的曲目数据中无有效曲目，程序退出\n");
        exit(EXIT_FAILURE);
    }
}

// -------------------------- 功能：dmenu 选择曲目 --------------------------
int show_dmenu(TrackList *tracks) {
    printf("\n共 %d 首曲目，启动dmenu选择...\n", (int)tracks->count);

    int pipe_in[2], pipe_out[2];
    if (pipe(pipe_in) == -1 || pipe(pipe_out) == -1) {
        perror("错误：创建管道失败");
        exit(EXIT_FAILURE);
    }

    pid_t pid = fork();
    if (pid == -1) {
        perror("错误：fork 失败（无法启动dmenu）");
        exit(EXIT_FAILURE);
    }

    if (pid == 0) { // 子进程：运行 dmenu
        close(pipe_in[1]);
        dup2(pipe_in[0], STDIN_FILENO);
        close(pipe_in[0]);

        close(pipe_out[0]);
        dup2(pipe_out[1], STDOUT_FILENO);
        close(pipe_out[1]);

        char *dmenu_args[] = {
            "dmenu", "-i", "-c", "-p", "选择曲目播放：",
            NULL // 必须以 NULL 结尾
        };
        execvp(dmenu_args[0], dmenu_args);
        perror("错误：dmenu 执行失败（请先安装dmenu）");
        exit(EXIT_FAILURE);
    }

    // 父进程：发送曲目名称到 dmenu
    close(pipe_in[0]);
    close(pipe_out[1]);

    for (size_t i = 0; i < tracks->count; i++) {
        dprintf(pipe_in[1], "%s\n", tracks->items[i].name);
    }
    close(pipe_in[1]);

    // 读取 dmenu 选择结果
    char selected_name[1024] = {0};
    ssize_t read_len = read(pipe_out[0], selected_name, sizeof(selected_name) - 1);
    printf("read_len: %ld\n", read_len);
    close(pipe_out[0]);

    // 等待 dmenu 结束
    int status;
    waitpid(pid, &status, 0);
    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        printf("用户取消选择或dmenu关闭\n");
        return -1;
    }

    trim_string(selected_name);
    if (strlen(selected_name) == 0) {
        printf("用户未选择曲目\n");
        return -1;
    }

    // 查找选择的曲目索引
    for (size_t i = 0; i < tracks->count; i++) {
        if (strcmp(selected_name, tracks->items[i].name) == 0) {
            printf("用户选择：%s（索引：%d）\n", selected_name, (int)i);
            return i;
        }
    }

    fprintf(stderr, "错误：未找到选择的曲目：%s（可能名称有特殊字符）\n", selected_name);
    return -1;
}

// -------------------------- 功能：创建缓存目录 --------------------------
void create_cache_dir(const char *dir) {
    char tmp[PATH_MAX];
    size_t len = strlen(dir);
    if (len >= PATH_MAX) {
        fprintf(stderr, "错误：缓存目录路径过长\n");
        exit(EXIT_FAILURE);
    }
    strcpy(tmp, dir);

    for (char *p = tmp + 1; *p; p++) {
        if (*p == '/') {
            *p = '\0';
            if (mkdir(tmp, 0755) == -1 && errno != EEXIST) {
                fprintf(stderr, "错误：创建目录 %s 失败：%s\n", tmp, strerror(errno));
                exit(EXIT_FAILURE);
            }
            *p = '/';
        }
    }

    if (mkdir(tmp, 0755) == -1 && errno != EEXIST) {
        fprintf(stderr, "错误：创建目录 %s 失败：%s\n", tmp, strerror(errno));
        exit(EXIT_FAILURE);
    }
}

// -------------------------- 功能：生成缓存文件名 --------------------------
char *get_cache_filename(const char *download_url, const char *cache_dir) {
    static char cache_file[PATH_MAX];
    char safe_url[2048];
    strncpy(safe_url, download_url, sizeof(safe_url) - 1);
    safe_url[sizeof(safe_url) - 1] = '\0';

    for (char *p = safe_url; *p; p++) {
        if (!isalnum((unsigned char)*p) && *p != '_' && *p != '-' && *p != '.') {
            *p = '_';
        }
    }

    snprintf(cache_file, sizeof(cache_file), "%s/%s.mp3", cache_dir, safe_url);
    return cache_file;
}

// -------------------------- 功能：发送桌面通知（无 system） --------------------------
void send_notification(const char *message) {
    if (message == NULL || strlen(message) == 0) return;

    // 转义单引号（避免命令参数错误）
    char escaped_msg[1024];
    size_t msg_len = strlen(message);
    size_t esc_idx = 0;
    for (size_t i = 0; i < msg_len && esc_idx < sizeof(escaped_msg) - 1; i++) {
        if (message[i] == '\'') {
            // 单引号转义为 '\''（关闭当前引号+转义单引号+重新打开引号）
            if (esc_idx + 3 >= sizeof(escaped_msg)) break;
            escaped_msg[esc_idx++] = '\'';
            escaped_msg[esc_idx++] = '\\';
            escaped_msg[esc_idx++] = '\'';
        } else {
            escaped_msg[esc_idx++] = message[i];
        }
    }
    escaped_msg[esc_idx] = '\0';

    // 执行 notify-send 命令
    char *notify_args[] = {
        "notify-send", "Chillhop Player", escaped_msg,
        NULL
    };
    execute_command(notify_args); // 忽略返回值（通知失败不影响主流程）
}

// -------------------------- 主函数 --------------------------
int main() {
    // 1. 清理残留 ffplay 进程（启动时执行）
    clean_ffplay_residues();

    // 2. 配置参数
    char *ffplay_path = "ffplay";
    char cache_dir[PATH_MAX];

    // 3. 初始化缓存目录
    const char *home = getenv("HOME");
    if (!home) {
        fprintf(stderr, "错误：未找到HOME环境变量（无法创建缓存目录）\n");
        exit(EXIT_FAILURE);
    }
    snprintf(cache_dir, sizeof(cache_dir), "%s/.cache/chillhop-player", home);
    create_cache_dir(cache_dir);
    printf("缓存目录：%s\n", cache_dir);

    // 4. 解析嵌入的曲目数据
    TrackList tracks;
    tracklist_init(&tracks);
    parse_embedded_tracks(&tracks);

    // 5. 启动dmenu选择曲目
    int selected_idx = show_dmenu(&tracks);
    if (selected_idx == -1) {
        tracklist_free(&tracks);
        return 0;
    }
    Track *selected = &tracks.items[selected_idx];

    // 6. 处理缓存和下载（用 curl 下载，无 system）
    char *cache_file = get_cache_filename(selected->download_url, cache_dir);
    struct stat st;
    if (stat(cache_file, &st) != 0) { // 缓存不存在
        char notify_msg[256];
        snprintf(notify_msg, sizeof(notify_msg), "正在下载：%s", selected->name);
        send_notification(notify_msg);
        printf("下载中：%s（URL：%s）\n", selected->name, selected->download_url);

        // 构建 curl 命令参数（-sSL 静默+跟随重定向+显示错误，-f 失败时返回非0）
        char *curl_args[] = {
            "curl", "-sSL", "-f",
            selected->download_url, // 源URL
            "-o", cache_file,       // 输出到缓存文件
            NULL
        };

        int download_ret = execute_command(curl_args);
        if (download_ret != 0) {
            send_notification("下载失败（URL无效或网络错误）");
            fprintf(stderr, "错误：下载失败：%s（请检查URL是否有效）\n", selected->download_url);
            remove(cache_file);
            tracklist_free(&tracks);
            return 1;
        }

        // 下载完成
        snprintf(notify_msg, sizeof(notify_msg), "下载完成：%s", selected->name);
        send_notification(notify_msg);
        printf("下载完成，缓存路径：%s\n", cache_file);
    } else { // 缓存已存在
        printf("使用缓存：%s\n", cache_file);
    }

    // 7. 播放曲目（用 ffplay，无 system）
    char play_msg[256];
    snprintf(play_msg, sizeof(play_msg), "开始播放：%s", selected->name);
    send_notification(play_msg);
    printf("正在播放：%s（按 Ctrl + C 退出）\n", selected->name);

    // 构建 ffplay 命令参数（-nodisp 无窗口，-autoexit 播放完退出）
    char *ffplay_args[] = {
        ffplay_path, "-nodisp", "-autoexit",
        cache_file,  // 播放缓存文件
        NULL
    };

    int play_ret = execute_command(ffplay_args);
    if (play_ret != 0) {
        fprintf(stderr, "警告：播放过程可能异常终止\n");
    }

    // 8. 播放结束
    send_notification("播放结束");
    printf("播放结束：%s\n", selected->name);

    // 9. 释放内存
    tracklist_free(&tracks);
    return 0;
}
